<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>手写call函数</title>
</head>
<body>
  <script>
    let obj={
      a:2,
      b:3
    }
    // function sum(){
    //   console.log(this.a,this.b)
    // }
    // sum(4,5)              // undefined undefined
    // sum.call(obj,4,5)     //  2 3

    // call方法的作用
    // 运用:函数.call(thisArg,arg1,arg2,……）
    //   改变this指向，将this指向thisArg

    // 手写call函数
    // 1：Function.prototype._call=function(context,...args){}
    // 2：函数内部先判断，如果context不是函数，抛出错误，如果不是对象，则构建一个对象，如果为空，则为window
    // 3：创建一个随机数key，给context添加一个属性，字段为随机数，指向调用call方法的函数context[key]=this;
    // 4：调用这个函数var result=context[key](...args);
    // 5：删除context[key]来复原，delete context[key];
    // 6：最后返回，return result;
    Function.prototype._call=function(context,...args){
      console.log(this);//调用call方法的是sum函数，所以this指向的是sum函数
      //如果传进来的是一个对象，但是这个对象是以Function.prototype为原型，也就是说可以调用函数上的call方法
      if(typeof this!=="function") {
          throw new TypeError("Type Error");
      }
      if(typeof context!==Object)  context=new Object(context);
      //如果传的第一个参数是null，那么内部的this指向的是window，在浏览器中是window，在nodejs中是global
      if(context==null) context=globalThis;//变成一个{}，空对象
      console.log(context);  // Object { a: 2, b: 3 }
      console.log(globalThis); // Window
      var key='fn_'+Math.random()*new Date().getTime();
      console.log(key)
      while(context.hasOwnProperty(key)){
        key='fn_'+Math.random()*new Date().getTime();
      }
      console.log(context.hasOwnProperty(key))
      //给context添加一个属性，指向调用call方法的函数
      context[key]=this;
      console.log(context.hasOwnProperty(key))
      //函数需要有参数传入,有一个值来接收，因为需要删除掉新加的属性
      console.log(...args)
      var result=context[key](...args);
      delete context[key];
      return result;
    }
    function sum(a,b,c){
      console.log(this.a,this.b,this.c);
    } 
    sum._call(obj,5,6); // 输出2，3，undefined
  </script>
</body>
</html>